package com.ruoyi.scrap.service.impl;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.AuditStatus;
import com.ruoyi.common.enums.DeleteStatus;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.scrap.domain.ScrapApply;
import com.ruoyi.scrap.domain.ScrapApplyDetail;
import com.ruoyi.scrap.domain.ScrapApplyInfo;
import com.ruoyi.scrap.domain.ScrapStockIn;
import com.ruoyi.scrap.domain.po.ScrapApplyPo;
import com.ruoyi.scrap.domain.vo.ScrapApplyAuditVo;
import com.ruoyi.scrap.domain.vo.ScrapApplyVo;
import com.ruoyi.scrap.mapper.ScrapApplyMapper;
import com.ruoyi.scrap.mapper.ScrapStockInMapper;
import com.ruoyi.scrap.service.ScrapApplyAuditService;
import com.ruoyi.scrap.service.ScrapApplyDetailService;
import com.ruoyi.scrap.service.ScrapApplyInfoService;
import com.ruoyi.scrap.service.ScrapApplyService;
import com.ruoyi.system.domain.SysAuditConfig;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.system.service.SysAuditConfigService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ScrapApplyServiceImpl extends ServiceImpl<ScrapApplyMapper, ScrapApply> implements ScrapApplyService {

    @Resource
    RedisCache redisCache;

    @Resource
    ISysDeptService deptService;

    @Resource
    ISysUserService userService;

    @Resource
    ScrapApplyInfoService infoService;

    @Resource
    ScrapApplyAuditService auditService;

    @Resource
    ScrapApplyDetailService detailService;

    @Resource
    SysAuditConfigService auditConfigService;

    @Resource
    ScrapStockInMapper stockInMapper;

    @Override
    public List<ScrapApply> getScrapApply(ScrapApply scrapApply) {
        return baseMapper.getScrapApply(scrapApply);
    }

    @Override
    public ScrapApplyPo getInfo(String applyId) {

        ScrapApply apply = baseMapper.selectById(applyId);

        if(!ObjectUtils.isEmpty(apply)){

            ScrapApplyInfo info = infoService.getById(applyId);

            if(!ObjectUtils.isEmpty(info)){

                ScrapApplyPo result = new ScrapApplyPo();

                BeanUtils.copyProperties(apply,result);

                BeanUtils.copyProperties(info,result);

                SysDept dept = deptService.selectDeptById(apply.getDeptId());

                if(!ObjectUtils.isEmpty(dept)){
                    result.setDeptName(dept.getDeptName());
                }

                SysUser nextAudit = userService.selectUserById(apply.getNextAudit());

                if(!ObjectUtils.isEmpty(nextAudit)){
                    result.setNextAuditName(nextAudit.getUserName());
                }

                result.setDetails(detailService.list(Wrappers.lambdaQuery(ScrapApplyDetail.class)
                        .eq(ScrapApplyDetail::getApplyId,applyId)
                        .eq(ScrapApplyDetail::getDelFlag,DeleteStatus.OK.getCode())));

                return result;

            }else{

                throw new BaseException("查询失败：未查询到编号为["+applyId+"]的申请单填单信息");

            }

        }

        throw new BaseException("查询失败：未查询到编号为["+applyId+"]的申请单信息");

    }

    /**
     * 提交或修改申请单
     *
     * @param applyVo
     * @param loginUser
     * @return
     */
    @Override
    @Transactional
    public boolean save(ScrapApplyVo applyVo, SysUser loginUser) {

        ScrapApply apply = new ScrapApply();

        if(StringUtils.isNotEmpty(applyVo.getApplyId())){

            ScrapApply info = baseMapper.selectById(applyVo.getApplyId());

            if(!info.getAuditStatus().equals("0") && !info.getAuditStatus().equals("2")){
                throw new BaseException("申请单号["+applyVo.getApplyId()+"】已进入审批流程，不允许修改");
            }

            detailService.remove(Wrappers.lambdaQuery(ScrapApplyDetail.class)
                    .eq(ScrapApplyDetail::getApplyId,applyVo.getApplyId()));

            synStockIn(applyVo.getApplyId(),"0");

        }else{

            applyVo.setApplyId(redisCache.getOrderNo(Constants.SCRAP_APPLY_IN_KEY));

            apply.setDelFlag(DeleteStatus.OK.getCode());
            apply.setCreateTime(new Date());
            apply.setCreateBy(loginUser.getUserName());
            apply.setLevel(0);

            if(ObjectUtils.isEmpty(applyVo.getWorkLeader())){
                apply.setNextAudit(applyVo.getDeptStaff());
            }else {
                apply.setNextAudit(applyVo.getWorkLeader());
            }

        }

        List<ScrapApplyDetail> details = new ArrayList<>();

        for(int i=0;i<applyVo.getDetails().size();i++){

            ScrapApplyDetail detail = new ScrapApplyDetail();

            BeanUtils.copyProperties(applyVo.getDetails().get(i),detail);

            detail.setDelFlag(DeleteStatus.OK.getCode());
            detail.setApplyId(applyVo.getApplyId());
            detail.setSortNo(i);

            details.add(detail);

        }

        detailService.saveBatch(details);

        synStockIn(applyVo.getApplyId(),"1");

        BeanUtils.copyProperties(applyVo,apply);

        apply.setId(applyVo.getApplyId());
        apply.setWorkTel(applyVo.getWorkTel());
        apply.setApplyTime(applyVo.getApplyTime());
        apply.setAuditStatus("0");
        apply.setUpdateTime(new Date());
        apply.setUpdateBy(loginUser.getUserName());
        apply.setDeptId(loginUser.getDeptId());
        apply.setWorkStaff(loginUser.getUserId());

        ScrapApplyInfo applyInfo = new ScrapApplyInfo();

        applyInfo.setApplyId(apply.getId());
        applyInfo.setWorkStaffName(loginUser.getNickName());
        applyInfo.setWorkStaffSign(applyVo.getSign());

        synName(applyInfo,apply);

        infoService.saveOrUpdate(applyInfo);

        return baseMapper.insertOrUpdate(apply);

    }

    private void synStockIn(String applyId,String status){

        List<ScrapApplyDetail> details = detailService.list(Wrappers.lambdaQuery(ScrapApplyDetail.class)
                .eq(ScrapApplyDetail::getApplyId,applyId));

        if(!ObjectUtils.isEmpty(details)){

            details.stream().forEach(x->{

                ScrapStockIn stockIn = new ScrapStockIn();

                stockIn.setStatus(status);
                stockIn.setId(x.getStockId());

                stockInMapper.updateById(stockIn);

            });

        }

    }

    @Override
    @Transactional
    public boolean remove(String[] ids, SysUser loginUser) {

        List<ScrapApply> remove = Arrays.asList(ids).stream().map(x->{

            ScrapApply apply = new ScrapApply();

            apply.setId(x);
            apply.setDelFlag(DeleteStatus.DELETED.getCode());
            apply.setUpdateTime(new Date());
            apply.setUpdateBy(loginUser.getUserName());

            synStockIn(x,"0");

            return apply;

        }).collect(Collectors.toList());

        if(ObjectUtils.isEmpty(remove)){
            throw new BaseException("请选择要删除的申请单");
        }

        ScrapApplyDetail detail = new ScrapApplyDetail();

        detail.setDelFlag(DeleteStatus.DELETED.getCode());

        detailService.update(detail,Wrappers.lambdaQuery(ScrapApplyDetail.class)
                .in(ScrapApplyDetail::getApplyId,Arrays.asList(ids)));

        return baseMapper.updateBatchById(remove);

    }

    /**
     *
     * 申请单审核
     *
     * @param auditVo
     * @param loginUser
     * @return
     */
    @Override
    @Transactional
    public int audit(ScrapApplyAuditVo auditVo, SysUser loginUser) {

        ScrapApply apply = baseMapper.selectById(auditVo.getApplyId());

        if(ObjectUtils.isEmpty(apply)){
            throw new BaseException("审核失败：未查询到单号["+auditVo.getApplyId()+"的申请单信息]");
        }

        if(!apply.getNextAudit().equals(loginUser.getUserId())){
            throw new BaseException("审核失败：当前登录账号与审核人信息不符，无权操作");
        }

        ScrapApply auditApply = new ScrapApply();

        auditApply.setId(auditVo.getApplyId());

        if(auditVo.getAuditStatus().equals(AuditStatus.OK.getCode())){

            auditVo.setReason("审核通过");

            List<SysAuditConfig> auditConfigs = auditConfigService.getNextAudit(apply.getLevel() +1,Constants.SCRAP_APPLY_AUDIT_CODE);

            if(!ObjectUtils.isEmpty(auditConfigs)){

                auditApply.setAuditStatus(AuditStatus.WAIT.getCode());
                auditApply.setLevel(apply.getLevel()+1);
                auditApply.setNextAudit(getNextAudit(apply,auditConfigs.get(0).getKey()));

            }else{

                auditApply.setAuditStatus(AuditStatus.OK.getCode());

                synStockIn(auditVo.getApplyId(),"2");

            }

        }else{

            auditApply.setAuditStatus(AuditStatus.REJECT.getCode());

            synStockIn(auditVo.getApplyId(),"3");

        }

        if(!ObjectUtils.isEmpty(auditVo.getDetails())){

            auditVo.getDetails().stream().forEach(x->{

                ScrapApplyDetail detail = new ScrapApplyDetail();

                detail.setId(x.getId());
                detail.setIdea(x.getIdea());

                detailService.updateById(detail);

            });

        }

        ScrapApplyInfo applyInfo = new ScrapApplyInfo();

        applyInfo.setApplyId(apply.getId());

        synSign(applyInfo, auditVo.getSign(), apply.getLevel());

        infoService.updateById(applyInfo);

        auditService.audit(auditVo,loginUser);

        auditApply.setReason(auditVo.getReason());
        auditApply.setUpdateBy(loginUser.getUserName());
        auditApply.setUpdateTime(new Date());
        if(!StringUtils.isEmpty(auditVo.getIdeaFile())){
            auditApply.setIdeaFile(auditVo.getIdeaFile());
        }

        return baseMapper.updateById(auditApply);

    }

    private long getNextAudit(ScrapApply apply,String key){

        try{

            Class<?> applyClass = apply.getClass();

            Field field = applyClass.getDeclaredField(key);

            if (!ObjectUtils.isEmpty(field)) {

                field.setAccessible(true);
                return (long) field.get(apply);

            }

            return 0;

        }catch (Exception e){

            throw new BaseException("审批失败：下一级审批人数据转换失败");

        }

    }

    private void synName(ScrapApplyInfo applyInfo,ScrapApply apply){

        if(!ObjectUtils.isEmpty(apply.getLeader())){

            SysUser user = userService.selectUserById(apply.getLeader());

            if(!ObjectUtils.isEmpty(user)){
                applyInfo.setLeaderName(user.getNickName());
            }

        }

        if(!ObjectUtils.isEmpty(apply.getWorkLeader())){

            SysUser user = userService.selectUserById(apply.getWorkLeader());

            if(!ObjectUtils.isEmpty(user)){
                applyInfo.setWorkLeaderName(user.getNickName());
            }

        }

        if(!ObjectUtils.isEmpty(apply.getDeptStaff())){

            SysUser user = userService.selectUserById(apply.getDeptStaff());

            if(!ObjectUtils.isEmpty(user)){
                applyInfo.setDeptStaffName(user.getNickName());
            }

        }

        if(!ObjectUtils.isEmpty(apply.getDeptLeader())){

            SysUser user = userService.selectUserById(apply.getDeptLeader());

            if(!ObjectUtils.isEmpty(user)){
                applyInfo.setDeptLeaderName(user.getNickName());
            }

        }

    }

    /**
     * 同步申请流程签字
     *
     * @param applyInfo
     * @param sign
     * @param level
     */
    private void synSign(ScrapApplyInfo applyInfo,String sign,Integer level){

        try {

            if (!ObjectUtils.isEmpty(sign)) {

                List<SysAuditConfig> auditConfigs = auditConfigService.getNextAudit(level, Constants.SCRAP_APPLY_AUDIT_CODE);

                if (ObjectUtils.isEmpty(auditConfigs)) {
                    throw new BaseException("审核失败：当前审核流程失效");
                }

                String key = auditConfigs.get(0).getKey();

                Field signField = ScrapApplyInfo.class.getDeclaredField(key + "Sign");

                if (!ObjectUtils.isEmpty(signField)) {

                    signField.setAccessible(true);
                    signField.set(applyInfo, sign);

                }

            }

        }catch (Exception e){

            throw new BaseException("审批失败：数据转换失败");

        }

    }

    @Override
    public JSONObject auditor(SysUser loginUser) {

        List<SysAuditConfig> configs = auditConfigService.getAuditParent(null,Constants.SCRAP_APPLY_AUDIT_CODE);

        return auditor(configs,loginUser.getDeptId().toString(),new JSONObject());

    }

    /**
     * 获取审核流程下一级审核人算法
     *
     * @param configs
     * @param deptId
     * @param result
     * @return
     */
    public JSONObject auditor(List<SysAuditConfig> configs,String deptId,JSONObject result){

        if(!ObjectUtils.isEmpty(configs)){

            configs.stream().forEach(config -> {

                List<Map> users;

                if(config.getType() == 0){

                    users = userService.getUserIdByPostAndDept(deptId,config.getPostId().toString());

                }else{

                    users = userService.getUserIdByPostAndDept(null,config.getPostId().toString());

                }

                List<JSONObject> auditor = users.stream().map(user->{

                    List<SysAuditConfig> child = auditConfigService.getAuditParent(config.getId().toString(),Constants.SCRAP_APPLY_AUDIT_CODE);

                    return auditor(child,user.get("deptId").toString(),JSONObject.parseObject(JSONObject.toJSONString(user)));

                }).collect(Collectors.toList());

                if(ObjectUtils.isEmpty(config.getParentId())){
                    result.put(config.getKey(),auditor);
                }else{
                    result.put("auditor",auditor);
                }

            });

        }

        return result;

    }

}
